package com.tedu.api.io;

import org.junit.Test;

import java.io.*;

/** IO流：传输数据
 * File文件，文件夹：无法读写数据
 * RandomAccessFile:读写按照字节，读写类不明确，靠构造器传入第二个参数确定
 *
 *  Java   提出一套 标准 IO --读写单独提供类
 * ①方向区别:
 *  输入流---(读：针对文件，文件往程序中走，文件“相当于程序”)
 *  输出流---(写：针对文件，文件写出，文件“相当于程序”)
 *  输入流和输出流创建方式?
 *  输入流父类：InputStream(抽象类)--顶级父类--读操作
 *  输出流：   OutputStream(抽象类)--顶级父类--写操作
 *  以上两个流定义所有流具备 读写操作，本身无法实例化
 * ②按照功能进行区别：
 * 低级流:专门搬运数据的流【又叫：节点流】
 * FileInputStream      文件输入流：读操作---InputStream(抽象类)
 * FileOutputStream     文件输出流: 写操作---OutputStream(抽象类)
 * 功能:建立在程序与数据源之间的“普通”，所有数据传输通过低级流【节点流】
 * 高级流：不能独立存在的流【叫做处理流】
 *        处理低级流的数据，并且可以被其他高级流所处理！
 *        理解：处理所搬运数据的流【高级流种类比较多】
 * ---------流：明确功能，创建对象，直接使用低级流---高级流创建
 *  项目:   聊天室  Version 1    IO流:针对传字符串
 */
public class IOAPI {
    @Test
    public void testIO() throws IOException {
//      // RandomAccessFile a=new RandomAccessFile("fos.txt","rw");
        //创建标准IO流，方向划分：输入流与输出流---功能：低级流【节点流】
        //需求：从项目中写出"fos.txt"  低级流：FileOutputStream
        //创建文件输出流  构造器("相对路径+文件全称")
        // 抛出一个异常FileNotFoundException：没有文件的异常，
        // 原因：路径或者文件名写错
        FileOutputStream fos=new FileOutputStream("fos.txt");
        /**
         * 输出一句话：我们是祖国的花朵
         * write(字符串：字符数组)    返回值：void
         * 作用：导出一个文件内容：字节数组
         * 抛出异常   IOException IO流异常:写出失败
         */
        //String 字符串 方法：getBytes() 可以将字符串转换成字符数组 char[]
        fos.write("我们是祖国的花朵".getBytes());
        //标准IO流必须关闭
        fos.close();//释放流：避免内存占用
    }
    /**
     * FileOutputStream实现类---构造器使用方法
     * new FileOutputStream(String path,boolean append)
     * 通过第二个参数，确定数据是否追加，true代表追加，反之
     *new FileOutputStream()
     */
    @Test
    public void testIOA() throws IOException {
        //当前 API    项目  下添加 fos.txt--辅助功能：追加效果
        //相对路径：./fos.txt    直接写文件|不写./ 都是当前目录下
        FileOutputStream fos=
                new FileOutputStream("fos.txt",true);
        String str="是未来";
        fos.write(str.getBytes());
        //关闭流
        fos.close();
    }
    /**低级流
     * 文件输入流
     * FileInputStream
     * read()  返回值：int
     * 作用：读取到一个字节
     */
    @Test
    public void testInputStream() throws IOException {
        //创建文件输入流----读入

        FileInputStream fis=
                new FileInputStream("fos.txt");
        //10KB   字节数组
        byte[] bytes=new byte[1024*10];
        //10KB  读   数据   【不想看到整数值】
        int len=fis.read(bytes);//len  返回 -1  文件读取完毕
        //String 字符串---提供构造器方法：按照字节数组量，输出字符串
        //String 构造器   new String (字节数据，开始读取文件，结束读取文件)
        //将【二进制】整数  转换为字符串
        String str=new String(bytes,0,len);//循环
    //一次性将给定的字节数组【10KB】写入到文件中，文件从0开始写入，到-1结束，写入完毕
        System.out.println("打印读："+str);
        //关闭流
        fis.close();
        }
       //需求：【标准IO】重构RandomAccessFile
    @Test
    public void testWork() throws IOException {
        //1.创建输入流---读---被复制文件【源文件】
        FileInputStream fis=
                new FileInputStream("特别长的视频.mp4");
        //2.创建输出流---写---复制品
        FileOutputStream fos =
                 new FileOutputStream("标准IO3.mp4");
        //3.用于保存读取到的字节量
        int len=1;//①保存读取的数据值【可以被覆盖】②-1代表文件结尾
        //6.视频空间大，解决方案：10KB，字节数组:设定保存值10KB
        byte[] bytes=new byte[1024*10];
        //4.一直读取【10KB】文件，保存len中，如果结果不等于-1，一直读
        //(len=fis.read(bytes))
        long start=System.currentTimeMillis();
        while ((len=fis.read(bytes))!=-1){
            //4.1将读取到的【10KB】文件写出
            fos.write(bytes,0,len) ;
        }
        long end=System.currentTimeMillis();
        System.out.println("测试时间："+(end-start)+"毫秒");
        //5.关闭流
        fis.close();
        fos.close();
    }
    /**
     * 高级流【处理流】：处理低级流以及其他高级流的以“种”流
     * 高级流不能独立存在，一定处理低级流
     * BufferedInputStream---高级流：快速读  缓冲输入流
     * BufferedInputStream---高级流：快速写  缓冲输出流
     * 以上两个高级流，内嵌一套按照实际传输数据大小，随机型改变的字节数组
     * 高级流语法：
     * BufferedInputStream bi=new BufferedInputStream(低级流引用)
     * 需求：视频 使用 IO流【低级流+高级流】---实现视频快速复制
     */
    @Test
    public void testCV() throws IOException {
        //创建低级流：输入流：高级流，输出流：高级流
        FileInputStream fis=new FileInputStream("特别长的视频.mp4");
        BufferedInputStream bis=new BufferedInputStream(fis);

        FileOutputStream fos=new FileOutputStream("缓冲文件.mp4");
        BufferedOutputStream bos=new BufferedOutputStream(fos);
        //2.定义整数变量保存每次读取的字节量
        int len=-1;
        //3.将高级流：缓冲输入流--读取的数据--保存给交len，结果不等于-1
        //(len=bis.read())!=-1
        long start=System.currentTimeMillis();
        //read()  读取【缓冲字符输入流：自动产生合理字节数组】--->读取
        while ((len=bis.read())!=-1){
            //write()  写出【缓冲字符输出流：自动产生合理字节数组】-->写出
            bos.write(len);
        }
        long end=System.currentTimeMillis();
        System.out.println("缓冲流花费的时间："+(end-start));
        //4.关闭流
        fis.close();
        fos.close();
    }

    /**[高级流：处理流]
     * BufferedOutputStream
     * 需求：写出一份文件：bos.txt  写出：一段文本内容
     * 所有的流，全部都不是正确的写法：IO流异常最多，采取捕获异常并做出处理
     */
    @Test
    public void testBOS() throws IOException {
        //创建低级流--写出
        FileOutputStream fos= null;
        //创建高级流--写出   一段文本内容[缓冲区：字节数组]
        BufferedOutputStream bos=null;
        try {
           //String str="50块钱再好看，也不如100块钱找人稀罕";
           //write(byte[] b)  作用：写出字节数组
           //bos.write(str.getBytes());
             fos=new FileOutputStream("文件");
             bos=new BufferedOutputStream(fos);
            /**
             *
             *    str数据量小      缓冲区大     强制送出【flush】   被动送出[close]
             *    45人            车 100            晚上9
             * flush()
             * 作用：可以强制将内部缓冲区的数据一次性读写
             * 缺点：频繁使用flush降低读写
             */
             bos.write("你好".getBytes());
             bos.flush();
           //bos.close();
           //fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bos!=null)
            try {
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 标准IO  必须与   异常   存在
     * 异常重点：1.认识代码中抛出异常【查API】，异常产生原因
     *         2.异常处理---【快捷键：ctrl+alt+T】
     *         语法：try{
     *             怀疑可能存在错误或者已经提示异常
     *         }catch(小异常名字 a){
     *             e.printStackTrace();   //打印异常信息---通过异常信息解决问题
     *         }catch(中异常名字 e){catch可以捕获许多异常:小到大
     *
     *         }
     *         finally{
     *             一定执行代码块
     *         }
     *  ----------------------
     *    异常大小问题:
     *    IO流：FileNotFoundException>IOException
     *         Exception顶级父类
     * ----------------------------------------
     *
     *    低级流：FileOutputStream  文件输出流----写出
     *           FileInputStream   文件输入流---读入
     *    高级流：BufferedOutputStream 缓冲输出流--写出--功能：缓冲区
     *           BufferedInputStream  缓冲输入流--读入--功能：缓冲区
     *
     *  考试题：final与finally的区别？
     *        final关键字：修饰  变量，方法，类-->变量成常量
     *                                       方法不能重写
     *                                       类不能被继承
     *        finally关键字：异常处理机制中：捕获异常
     *                 finally在捕获异常中，可以写，也可以不写
     *     写finally关键字所有异常都不符合，一定会执行的语法快
     *     通常使用在IO流中，关闭流为主！关闭高级流--->间接关闭低级流
     *
     *     需求：BOS高级流---重新结合异常--->标准IO流
     */
    @Test
    public void testBOSIO(){
        //1.需要将流先置空：置空--①覆盖，不用多次创建对象！②关闭流
        FileOutputStream fos=null;///低级流---搬运数据  写出
        BufferedOutputStream bos=null;//高级流--处理低级流：缓冲输出流
        //2.捕获异常操作:快捷键 alt+ctrl+T     怀疑IO流代码--->捕获异常
        //2.1创建输出流
        try {
            fos=new FileOutputStream("BOS标准流写法.txt");
            bos=new BufferedOutputStream(fos);
            //3.高级流：缓冲输出流---功能：缓冲区【字节数组】    写出数据
            bos.write("标准流写法".getBytes());
            //4.缓冲区：数据量比较小----采用flush()   数据一次性写出
            bos.flush();
        } catch (FileNotFoundException e) {//文件不存在异常：路径写错，文件不存在
            e.printStackTrace();//打印异常信息
            System.out.println("【小】文件不存在异常");
        }catch(IOException e){//IO流最大的异常：小异常不处理
            e.printStackTrace();
            System.out.println("【小】IO流异常");
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("【大】异常");
        } finally {
            //一定执行代码块：IO流关闭---高级流--关闭高级流--低级流关闭【捕获异常】
            //                       低级流--关闭低级流【捕获异常】
            //5.先判断，高级流--是否为空【有没有高级流对象】---有高级流对象【必须关闭】
            //                                       没有高级流对象【不必关闭】
            if (bos!=null)
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
}

